package com.liqiye.paymanager.service.payment;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liqiye.paymanager.bean.payment.*;
import com.liqiye.paymanager.config.PaymentProperties;
import com.liqiye.paymanager.constant.OrderStatus;
import com.liqiye.paymanager.constant.PaymentPlatform;
import com.liqiye.paymanager.constant.WebhookEvent;
import com.liqiye.paymanager.entity.*;
import com.liqiye.paymanager.service.common.*;
import com.liqiye.paymanager.util.CurrencyConverterUtil;
import com.liqiye.paymanager.util.StripeClientUtil;
import com.stripe.StripeClient;
import com.stripe.exception.SignatureVerificationException;
import com.stripe.exception.StripeException;
import com.stripe.model.*;
import com.stripe.model.identity.VerificationSession;
import com.stripe.net.RequestOptions;
import com.stripe.net.Webhook;
import com.stripe.param.*;
import com.stripe.param.identity.VerificationSessionCreateParams;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Service
@Slf4j
public class StripePaymentService implements PaymentService {

    private final PaymentProperties config;
    private final StripeClient stripeClient;
    private final StripeIntentService stripeIntentService;
    private final StripeCustomerService stripeCustomerService;
    private final OrderService orderService;
    private final StripeSubscriptionService stripeSubscriptionService;
    private final StripePayoutService stripePayoutService;

    public StripePaymentService(PaymentProperties config,
                                StripeClientUtil stripeClientUtil,
                                StripeIntentService stripeIntentService,
                                StripeCustomerService stripeCustomerService,
                                OrderService orderService,
                                StripeSubscriptionService stripeSubscriptionService,
                                StripePayoutService stripePayoutService) {

        this.config = config;
        this.stripeClient = stripeClientUtil.getClient();
        this.stripeIntentService = stripeIntentService;
        this.stripeCustomerService = stripeCustomerService;
        this.orderService = orderService;
        this.stripeSubscriptionService = stripeSubscriptionService;
        this.stripePayoutService = stripePayoutService;
    }

    /**
     * 在 Stripe 中，是发起一个 intent
     * 返回的 clientSecret 给到前端发起支付请求
     */
    @Override
    public PaymentResponse createPayment(PaymentRequest req) {
        PaymentResponse resp = new PaymentResponse();
        try {
            // stripe 创建 intent
            long amount = CurrencyConverterUtil.amountToMinimun(req.getAmount(), req.getCurrency());
            PaymentIntentCreateParams params = PaymentIntentCreateParams.builder()
                    .setAmount(amount)  // 注意这个是最小货币单位，例如美元，这里就是美分
                    .setCurrency(req.getCurrency())
                    .addPaymentMethodType(req.getPaymentMethod().toLowerCase())
                    .build();
            PaymentIntent intent;
            if(StrUtil.isNotBlank(req.getIdempotencyKey())){
                // 设置幂等性键
                RequestOptions requestOptions = RequestOptions.builder()
                        .setIdempotencyKey(req.getIdempotencyKey())
                        .build();
                intent = stripeClient.paymentIntents().create(params, requestOptions);
            }else{
                intent = stripeClient.paymentIntents().create(params);
            }
            String paymentIntentId = intent.getId();
            log.info("paymentIntentId:" + paymentIntentId);
            resp.success = true;
            resp.clientSecret = intent.getClientSecret();

            // db 记录
            stripeIntentService.createIntent(intent, req);

        } catch (Exception e) {
            e.printStackTrace();
            resp.success = false;
        }

        return resp;
    }

    @Override
    public PaymentStatusResponse queryPayment(String platformOrderId) {
        // Stripe API: GET /v1/payment_intents/{id}
        return null;
    }

    @Override
    public CaptureResponse captureOrder(String orderId) {

        return null;
    }

    @Override
    public RefundResponse refund(RefundRequest req) {
        RefundResponse refundResponse = new RefundResponse();
        try {
            String chargeId = req.getChargeId();
            String paymentIntentId = req.getPaymentIntentId();

            if (StrUtil.isAllBlank(chargeId, paymentIntentId)) {
                throw new IllegalArgumentException("退款请求必须包含 chargeId 或 paymentIntentId");
            }

            // stripe 退款
            RefundCreateParams.Builder builder = RefundCreateParams.builder();
            if (StrUtil.isNotBlank(chargeId)) {
                builder.setCharge(chargeId);
            } else {
                builder.setPaymentIntent(paymentIntentId);
            }
            // builder.setAmount(500L);  // 可以部分退款，不指定就是全退（这个是最小货币单位，例如美元，这里就是美分）
            RefundCreateParams params = builder.build();
            Refund refund = stripeClient.refunds().create(params);


            // db 记录
            QueryWrapper<StripeIntent> wrapper = new QueryWrapper<>();
            if (StrUtil.isNotBlank(chargeId)) {
                wrapper.eq("charge_id", chargeId);
            } else {
                wrapper.eq("id", paymentIntentId);
            }
            StripeIntent stripeIntent = stripeIntentService.getOne(wrapper);
            Order order = orderService.getById(stripeIntent.getOrderId());
            order.setStatus(OrderStatus.REFUND.name());
            orderService.updateById(order);

            refundResponse.success = true;
            refundResponse.refundId = refund.getId();
            if(refund.getAmount() != null){
                refundResponse.refundedAmount = refund.getAmount().toString();
            }
            refundResponse.refundCurrency = refund.getCurrency();
            refundResponse.status = refund.getStatus();
            refundResponse.message = "Refund success";
            return refundResponse;

        } catch (Exception e){

            e.printStackTrace();
            refundResponse.success = false;
            if(req.amount != null) {
                refundResponse.refundedAmount = req.amount.toString();
            }
            refundResponse.refundCurrency = req.currency;
            refundResponse.message = "Refund failed: " + e.getMessage();
            return refundResponse;
        }
    }

    @Override
    public CallbackResponse handleCallback(String payload, String signature) {
        // 校验 Stripe Webhook 签名
        return null;
    }

    public CustomerResponse createCustomer(CreateCustomerRequest req){
        CustomerCreateParams params = CustomerCreateParams.builder()
                        .setDescription(req.description)
                        .setEmail(req.getEmail())
                        .setName(req.getName())
                        .setPaymentMethod(req.paymentMethod)  // obtained via Stripe.js
                        .build();

        try {
            // stripe 创建 customer
            Customer customer = stripeClient.customers().create(params);
            log.info("创建客户：" + customer);


            // db 记录
            StripeCustomer stripeCustomer = new StripeCustomer();
            BeanUtil.copyProperties(customer, stripeCustomer, true);
            stripeCustomer.setUpdateTime(new Date());
            stripeCustomer.setOrderId(req.getOrderId());
            stripeCustomerService.save(stripeCustomer);


            CustomerResponse resp = new CustomerResponse();
            BeanUtil.copyProperties(customer, resp, true);
            return resp;
        } catch (StripeException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 记录charge
     * 一个intent可能存在多个change扣款记录
     */
    public void recordCharge(String paymentIntentId){
        try {
            // 请求 stripe 获取 latestChargeId
            PaymentIntent intent = stripeClient.paymentIntents().retrieve(paymentIntentId);
            String latestChargeId = intent.getLatestCharge();
            log.info("chargeId:" + latestChargeId);

            // db 记录
            StripeIntent stripeIntent = stripeIntentService.getById(paymentIntentId);
            stripeIntent.setLatestChargeId(latestChargeId);
            stripeIntentService.updateById(stripeIntent);
            Order order = orderService.getById(stripeIntent.getOrderId());
            order.setStatus(OrderStatus.PAID.name());
            order.setPayPlatform(PaymentPlatform.STRIPE.name());
            orderService.updateById(order);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public PaymentResponse createSetupIntent(CreateSetupIntentRequest request){
        PaymentResponse resp = new PaymentResponse();
        try {
            // 创建 customer
            CustomerCreateParams customerCreateParams = CustomerCreateParams.builder()
                    .setName(request.getName())
                    .setEmail(request.getEmail())
                    .setDescription(request.getDescription())
                    .build();
            Customer customer = stripeClient.customers().create(customerCreateParams);
            // log.info("customerId:" + customer.getId());

            // 创建 setupIntent
            SetupIntentCreateParams setupIntentParams = SetupIntentCreateParams.builder()
                    .setCustomer(customer.getId())
                    .addPaymentMethodType("card")
                    .build();
            SetupIntent setupIntent = stripeClient.setupIntents().create(setupIntentParams);


            // db 记录
            StripeCustomer stripeCustomer = new StripeCustomer();
            BeanUtil.copyProperties(customer, stripeCustomer, true);
            stripeCustomer.setUpdateTime(new Date());
            stripeCustomerService.save(stripeCustomer);
            StripeIntent stripeIntent = new StripeIntent();
            stripeIntent.setId(setupIntent.getId());
            stripeIntent.setUpdateTime(new Date());
            stripeIntent.setSetup(true);
            stripeIntentService.save(stripeIntent);


            resp.success = true;
            resp.clientSecret = setupIntent.getClientSecret();
            resp.customerId = customer.getId();
            return resp;

        } catch (Exception e) {
            e.printStackTrace();
            resp.success = false;
            return resp;
        }
    }

    public void createSubscription(CreateSubscriptionRequest request){
        try {
            // stripe 给 customer 绑定这个 paymentMethodId（也可以不写这一段，后续会自动对应上这个）
            PaymentMethodAttachParams attachParams = PaymentMethodAttachParams.builder()
                    .setCustomer(request.getCustomerId())  // 已创建的 customer ID
                    .build();
            PaymentMethod paymentMethod = stripeClient.paymentMethods().retrieve(request.getPaymentMethodId());
            PaymentMethod attachedMethod = paymentMethod.attach(attachParams);
            if (attachedMethod.getCustomer() == null || !attachedMethod.getCustomer().equals(request.getCustomerId())) {
                throw new IllegalStateException("PaymentMethod 没有成功绑定到 Customer");
            }


            // stripe 创建 Subscription 订阅
            SubscriptionCreateParams.PaymentSettings paymentSettings = SubscriptionCreateParams.PaymentSettings.builder()
                            .addPaymentMethodType(SubscriptionCreateParams.PaymentSettings.PaymentMethodType.CARD)
                            .setSaveDefaultPaymentMethod(SubscriptionCreateParams.PaymentSettings.SaveDefaultPaymentMethod.ON_SUBSCRIPTION)
                            .build();
            SubscriptionCreateParams.Item itemParams = SubscriptionCreateParams.Item.builder()
                    .setPrice(config.getStripe().getPriceId().getUSD10PerDay()) // Stripe 后台设置好的价格 ID（如 9.99 美元/月）
                    .build();
            SubscriptionCreateParams params = SubscriptionCreateParams.builder()
                    .addItem(itemParams)
                    .setPaymentSettings(paymentSettings)
                    .setCustomer(request.getCustomerId())
                    .setDefaultPaymentMethod(request.getPaymentMethodId())
                    .setCollectionMethod(SubscriptionCreateParams.CollectionMethod.CHARGE_AUTOMATICALLY)  // 自动扣款
                    .setTrialEnd(SubscriptionCreateParams.TrialEnd.NOW)  // 不要试用期
                    .build();
            Subscription subscription = stripeClient.subscriptions().create(params);
            log.info("subscriptionId:" + subscription.getId());


            // db 记录
            StripeSubscription stripeSubscription = new StripeSubscription();
            stripeSubscription.setId(subscription.getId());
            stripeSubscription.setCustomerId(request.getCustomerId());
            stripeSubscription.setPaymentMethodId(request.getPaymentMethodId());
            stripeSubscriptionService.save(stripeSubscription);


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void cancelSubscription(String subscriptionId){
        try {
            // 立即取消该订阅
            SubscriptionCancelParams cancelParams = SubscriptionCancelParams.builder()
                    .setInvoiceNow(false)                  // 不立刻发账单
                    .build();
            Subscription subscription = stripeClient.subscriptions().cancel(subscriptionId, cancelParams);


            // // 周期末取消该订阅
            // SubscriptionUpdateParams updateParams = SubscriptionUpdateParams.builder()
            //         .setCancelAtPeriodEnd(true)
            //         .build();
            // Subscription subscription = stripeClient.subscriptions().update(subscriptionId, updateParams);


            log.info("取消成功，状态：" + subscription.getStatus());
        } catch (StripeException e) {
            e.printStackTrace();
        }
    }


    /**
     * stripe 的 webhook 接口
     * 可以用来实现扣款后的通知，订阅付款的通知等等
     */
    public ResponseEntity handleWebhook(HttpServletRequest request) {
        String endpointSecret = config.getStripe().getWebhookSecret();

        String payload;
        String sigHeader = request.getHeader("Stripe-Signature");

        try {
            payload = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Invalid payload");
        }

        Event event;
        try {
            event = Webhook.constructEvent(payload, sigHeader, endpointSecret);
        } catch (SignatureVerificationException e) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Signature verification failed");
        }

        if (WebhookEvent.INVOICE_PAID.equals(event.getType())) {
            EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer();
            Optional<StripeObject> obj = dataObjectDeserializer.getObject();
            if (obj.isPresent()) {
                Invoice invoice = (Invoice) obj.get();

                String customerId = invoice.getCustomer();
                Long amountPaid = invoice.getAmountPaid();  // 单位：分
                String currency = invoice.getCurrency();
                // String subscriptionId = (String) invoice.getRawJsonObject().get("subscription");
                // String paymentIntentId = (String) invoice.getRawJsonObject().get("payment_intent");


                log.info("==== Stripe 订阅自动扣款成功，发送短信通知 ====");
                log.info("客户 ID: " + customerId);
                log.info("扣款金额: " + (amountPaid / 100.0) + " " + currency.toUpperCase());
                // System.out.println("订阅 ID: " + subscriptionId);
                // System.out.println("PaymentIntent ID: " + paymentIntentId);
            }
        }

        if (WebhookEvent.IDENTITY_VERIFICATION_SESSION_VERFIED.equals(event.getType())) {
            VerificationSession session = (VerificationSession) event.getDataObjectDeserializer()
                    .getObject().orElse(null);
            if (session != null) {
                String userId = session.getMetadata().get("user_id");
                System.out.println("✅ 用户已完成身份验证: " + userId);
                // TODO: 更新用户数据库状态（是否是绿色用户，还是存在风险的用户）
            }
        }

        return ResponseEntity.ok("success");
    }

    public Map<String, Object> queryBalance() throws StripeException {
        Balance balance = stripeClient.balance().retrieve();

        List<Map<String, Object>> availableList = new ArrayList<>();
        for (Balance.Available available : balance.getAvailable()) {
            Map<String, Object> m = new HashMap<>();
            m.put("currency", available.getCurrency());
            m.put("amount", available.getAmount());
            m.put("sourceTypes", available.getSourceTypes());
            availableList.add(m);
        }

        List<Map<String, Object>> pendingList = new ArrayList<>();
        for (Balance.Pending pending : balance.getPending()) {
            Map<String, Object> m = new HashMap<>();
            m.put("currency", pending.getCurrency());
            m.put("amount", pending.getAmount());
            m.put("sourceTypes", pending.getSourceTypes());
            pendingList.add(m);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("available", availableList);
        result.put("pending", pendingList);
        result.put("livemode", balance.getLivemode());

        return result;
    }


    /**
     * 发起提现到 Stripe 账户绑定的银行账户
     */
    public void withdraw(WithdrawRequest request) throws StripeException {
        BigDecimal amount = request.getAmount();
        String currency = request.getCurrency();

        // stripe 检查可用余额是否足够
        Balance balance = stripeClient.balance().retrieve();
        Long available = balance.getAvailable().stream()
                .filter(b -> b.getCurrency().equalsIgnoreCase(currency))
                .map(b -> b.getAmount())
                .findFirst()
                .orElse(0L);
        if (available < amount.longValue()) {
            throw new IllegalArgumentException(currency + "余额不足，当前余额：" + available + "，请求提现：" + amount);
        }

        // stripe 提现
        PayoutCreateParams params = PayoutCreateParams.builder()
                .setAmount(amount.longValue())
                .setCurrency(currency)
                .build();
        Payout payout = stripeClient.payouts().create(params);
        log.info("提现成功，payoutId={}", payout.getId());


        // db 记录
        StripePayout stripePayout = new StripePayout();
        BeanUtil.copyProperties(payout, stripePayout, true);
        stripePayoutService.save(stripePayout);

    }

    public VerificationSession createVerificationSession(String userId) throws Exception {
        VerificationSessionCreateParams.Options.Document documentParam = VerificationSessionCreateParams.Options.Document.builder()
                .setRequireMatchingSelfie(true)  // 开启人脸对比
                .build();
        VerificationSessionCreateParams.Options optionsParam = VerificationSessionCreateParams.Options.builder()
                .setDocument(documentParam)
                .build();
        VerificationSessionCreateParams createParams = VerificationSessionCreateParams.builder()
                .setType(VerificationSessionCreateParams.Type.DOCUMENT)  // 表示验证政府证件
                .setOptions(optionsParam)
                .putMetadata("user_id", userId)  // 这个是我们业务系统的id，用来在webhook接口识别是我们的哪个用户
                .build();

        VerificationSession verificationSession = stripeClient.identity().verificationSessions().create(createParams);

        return verificationSession;
    }






}
